/*
 * Copyright 2008 Wink Saville
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.saville.mc;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import com.saville.msgcomp.Msg;
import com.saville.msgcomp.McConst;

/*
 * Looper is the message pump for the thread
 */
public class MsgPump {
    /*
     * Constructor is not visible, call prepare
     */
    private MsgPump() {
    }
    
    /*
     * Prepare the looper
     */
    public static void prepare() {
        if (MsgPump.mTlsLooper.get() != null) {
            throw new RuntimeException("Looper.prepare must only be called once");
        }
        MsgPump.mTlsLooper.set(new MsgPump());
    }
    
    /*
     * Loop processing messages.
     */
    public static void loop() {
        MsgPump msgPump = MsgPump.myMsgPump();
        
        while (true) {
            try {
                Msg msg = msgPump.mMsgs.take();
                if (msgPump.mDone.get())
                    break;
                msg.handler.processMsg(msg);
            } catch (InterruptedException e) {
            }
        }
        
        /*
         * TODO: We should probably reply not just drop??
         */
        msgPump.mMsgs.clear();
    }
    
    /*
     * Stop as gracefully as possible this thread.
     */
    public void stopProcessing() {
        mDone.set(true);
        Msg msg = new Msg();
        msg.guid = McConst.MC_GUID;
        msg.cmd = McConst.MC_CMD_NOP;
        queueMsg(msg);
    }
    
    /*
     * Queue a message to the msg queue
     */
    public void queueMsg(Msg msg) {
        try {
            mMsgs.put(msg);
        } catch (InterruptedException e) {
            new RuntimeException("Looper.queueMsg(): exception");
        }
    }
    
    /*
     * Return this threads instance of looper
     */
    public static MsgPump myMsgPump() {
        return (MsgPump)MsgPump.mTlsLooper.get();
    }
    
    /*
     * The thread local storage for the looper
     * 
     */
    private static ThreadLocal<MsgPump> mTlsLooper = new ThreadLocal<MsgPump>();

    /*
     * Set to true to halt execution of Looper
     */
    private AtomicBoolean mDone = new AtomicBoolean(false);
    
    /*
     * The list of messages pending to be processed
     */
    private LinkedBlockingQueue<Msg> mMsgs = new LinkedBlockingQueue<Msg>();
}
